/*
 * Copyright (C) 2019-2020 FoilPlanet
 *
 */

#include "anbox/audio/sink.h"
#include "anbox/graphics/buffer_queue.h"
#include "anbox/input/device.h"
#include "anbox/input/manager.h"
#include "anbox/logger.h"
#include "anbox/platform/gbm/platform.h"
#include "anbox/wm/window.h"

extern "C" {
#include "common.h"
#include "drm-common.h"
}

using namespace anbox::platform::gbm;

namespace audio    = anbox::audio;
namespace input    = anbox::input;
namespace graphics = anbox::graphics;
namespace video    = anbox::video;
namespace wm       = anbox::wm;

static const struct gbm *s_gbm;
static const struct drm *s_drm;

class KmsWindow : public wm::Window {
  public:
    KmsWindow(const std::shared_ptr<Renderer> &renderer,
              const uint32_t id,
              const wm::Task::Id &task,
              const graphics::Rect &frame,
              const std::string &title,
              bool  resizable)
      : wm::Window(renderer, task, frame, title), id_(id)
    {
      (void)resizable;
    }

  private:
    uint32_t id_;
};

class GbmAudioSink : public audio::Sink {
 public:
  GbmAudioSink() {};
  ~GbmAudioSink() {};

  void write_data(const std::vector<std::uint8_t> &data) override {
    (void)data;
    // TODO
  }
};


Platform::Platform(const std::shared_ptr<input::Manager> &input_manager,
                   const graphics::Rect &static_display_frame,
                   bool single_window)
  : input_manager_(input_manager), 
    display_frame_(static_display_frame),
    single_window_(single_window)
{
  const char *device = getenv("GBM_DEVICE");  // "/dev/dri/card1", NULL to probe
  char mode_str[DRM_DISPLAY_MODE_LEN] = "";
	unsigned int vrefresh = 0;
	unsigned int count = ~0;
  bool use_atomic = false;

  INFO("Using GBM device '%s'", device == NULL ? "*" : device);
  if (use_atomic) {
    s_drm = init_drm_atomic(device, mode_str, vrefresh, count);
  } else {
    s_drm = init_drm_legacy(device, mode_str, vrefresh, count);
  }

  if (!s_drm) {
    ERROR("Failed to initialize %s DRM", use_atomic ? "atomic" : "legacy");
    BOOST_THROW_EXCEPTION(std::runtime_error("Platform - Failed to initialize GBM device"));
  }

  uint32_t format   = DRM_FORMAT_ABGR8888; // DRM_FORMAT_XRGB8888
  uint64_t modifier = DRM_FORMAT_MOD_LINEAR;

  s_gbm = init_gbm(s_drm->fd, s_drm->mode->hdisplay, s_drm->mode->vdisplay,
                   format, modifier);

  if (!s_gbm) {
    ERROR("Failed to initialize GBM");
  } else {
    INFO("Found DRM device %X", s_gbm->dev);
  }

  // initialize egl with s_gbm->dev

  // ::Renderer::queryDisplay(EGL_PLATFORM_GBM_KHR /* EGL_PLATFORM_DEVICE_EXT */ );

#ifdef ENABLE_TOUCH_INPUT
  auto touch_ = input_manager->create_device(true); // enable monitor
  touch_->set_name("anbox-touch");
  touch_->set_driver_version(1);
  touch_->set_input_id({BUS_VIRTUAL, 4, 4, 4});
  touch_->set_physical_location("none");
  touch_->set_key_bit(BTN_TOUCH);
  touch_->set_key_bit(BTN_TOOL_FINGER);

  touch_->set_abs_bit(ABS_MT_SLOT);
  touch_->set_abs_max(ABS_MT_SLOT, 10);
  touch_->set_abs_bit(ABS_MT_TOUCH_MAJOR);
  touch_->set_abs_max(ABS_MT_TOUCH_MAJOR, 127);
  touch_->set_abs_bit(ABS_MT_TOUCH_MINOR);
  touch_->set_abs_max(ABS_MT_TOUCH_MINOR, 127);
  touch_->set_abs_bit(ABS_MT_POSITION_X);
  touch_->set_abs_max(ABS_MT_POSITION_X, display_frame_.width());
  touch_->set_abs_bit(ABS_MT_POSITION_Y);
  touch_->set_abs_max(ABS_MT_POSITION_Y, display_frame_.height());
  touch_->set_abs_bit(ABS_MT_TRACKING_ID);
  touch_->set_abs_max(ABS_MT_TRACKING_ID, 10);
  touch_->set_prop_bit(INPUT_PROP_DIRECT);
#endif
}

uint32_t Platform::next_window_id() {
  static uint32_t next_id = 0;
  return next_id++;
}

std::shared_ptr<wm::Window>
Platform::create_window(const wm::Task::Id &task, 
                        const graphics::Rect &frame,
                        const std::string &title)
{
  if (!renderer_) {
    ERROR("Can't create window without a renderer set");
    return nullptr;
  }

  auto id = next_window_id();
  auto w = std::make_shared<KmsWindow>(renderer_, id, task, frame, title, false);
  windows_.insert({id, w});
  return w;
}

void Platform::set_clipboard_data(const ClipboardData &data)
{
  (void)data;
  ERROR("Not implemented");
}

Platform::ClipboardData Platform::get_clipboard_data()
{
  ERROR("Not implemented");
  return ClipboardData{};
}

std::shared_ptr<audio::Sink> Platform::create_audio_sink()
{
  return std::make_shared<GbmAudioSink>();
}

std::shared_ptr<audio::Source> Platform::create_audio_source()
{
  ERROR("Not implemented");
  return nullptr;
}

void *Platform::get_display()
{
  return (s_gbm == NULL) ? EGL_DEFAULT_DISPLAY : s_gbm->dev;
}


#include "OpenGLESDispatch/EGLDispatch.h"

extern EGLDispatch s_egl;

extern "C" {

EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
  // return s_egl.eglSwapBuffers(display, surface);
  (void)display;
  (void)surface;
  return false;
}

void finish_perfcntrs(void) {
  // TODO
}

void dump_perfcntrs(unsigned nframes, uint64_t elapsed_time_ns) {
  (void)nframes;
  (void)elapsed_time_ns;
}

}
